/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package com.emex.app.stuff.security.ui;

import com.emex.app.stuff.security.SecurityManager;
import com.emex.app.stuff.utils.SpringUtilities;
import com.emex.app.stuff.utils.Validation;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.prefs.Preferences;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import org.jdesktop.swingx.JXHyperlink;
import org.jdesktop.swingx.JXTextField;
import org.openide.LifecycleManager;
import org.openide.util.NbPreferences;

/**
 * Login dialog for desktop application
 *
 * @author Boris Larenyshev
 */
public class LoginDialog extends JDialog {

    private static final String USERNAME_TOOLTIP = "Введите логин";
    private static final String SHOW_PARAMS = "параметры соединения";
    private static final long serialVersionUID = 1L;
    protected JXTextField userName = null;
    protected JPasswordField userPassword = null;
    private JButton loginBtn = null;
    private JButton cancelBtn = null;
    private JButton paramsBtn = null;
    private JPanel mainPanel, loginPanel, paramsPanel, buttonPanel;
    private final ImageIcon topImage;
    private static final long TIME_FOR_REPEAT_UPDATE = 3600L * 1000;
    private static final String PORT_FOR_UPDATE_CENTER = "8081";
    private String addressForUpdateCenter = "";
    /**
     * Flag shows open expand or collapse additional params panel
     */
    private boolean isShownParams = false;
    /**
     * Additional connect parameters and their input fields
     */
    private final Map<ConnectionParams, JTextField> map = new HashMap<>();
    /**
     * app version
     */
    private String version = "";
    /**
     * calling stored preferences associated with this class from platform
     */
    Preferences node = NbPreferences.forModule(this.getClass());

    /**
     * Login actions
     *
     * @return true if success login into system
     */
    protected boolean login() {
        SecurityManager.getDefault().setEndpointAddress(
                getPropertyValue(ConnectionParams.SERVER_ADDRESS) + ":"
                + getPropertyValue(ConnectionParams.SERVER_PORT));
        return (SecurityManager.getDefault().wsLogin(getUsername(), getPassword()));
    }

    /**
     * Actions performed after successful login
     *
     */
    protected void doLogin() {
        saveValues();
    }

    /**
     * Save login and additional connection params to props
     *
     */
    protected void saveValues() {
        node.put("user.login", userName.getText().trim());
        for (ConnectionParams param : map.keySet()) {
            node.put(param.getPropName(), map.get(param).getText().trim());
        }
    }

    /**
     * Restore login and additional connection params from props
     *
     */
    protected void restoreValues() {
        setUsername(node.get("user.login", ""));
        for (ConnectionParams param : map.keySet()) {
            map.get(param).setText(node.get(param.getPropName(), ""));
        }
    }

    /**
     * Actions performed after login canceling
     *
     */
    protected void doCancel() {
        LifecycleManager.getDefault().exit();
    }

    /**
     * Constructor
     *
     * @param title dialog title
     * @param version app version
     */
    public LoginDialog(String title, String version) {
        setTitle(title + " : Вход в систему");
        setModal(true);
        setModalityType(ModalityType.APPLICATION_MODAL);

        this.topImage = new ImageIcon(LoginDialog.class.getResource("/com/emex/app/stuff/gui/splash.gif"));
        this.version = "версия " + version;

        setLayout(new BorderLayout());
        setResizable(false);

        createTop();
        createPanel();
        createButtons();

        initActions();

        pack();
        setLocationRelativeTo(null);
    }

    private void createTop() {
        JPanel p = new JPanel();
        p.setLayout(new BorderLayout());

        JLabel imgLabel = new VersionLabel(topImage, version);
        imgLabel.setBorder(new LineBorder(Color.gray, 1));
        imgLabel.setName("LoginDialog.imgLabel");

        p.add(imgLabel);

        add(p, BorderLayout.NORTH);
    }

    private void createPanel() {
        mainPanel = new JPanel();
        mainPanel.setLayout(new SpringLayout());
        mainPanel.setName("LoginDialog.mainPanel");

        createLoginPanel();
        createButtonPanel();
        createParamsPanel();

        mainPanel.add(loginPanel);
        mainPanel.add(buttonPanel);
        SpringUtilities.makeCompactGrid(mainPanel, 2, 1, // rows, cols
                20, 0, // initX, initY
                5, 0); // xPad, yPad
        add(mainPanel, BorderLayout.CENTER);
    }

    private void createLoginPanel() {
        loginPanel = new JPanel();
        loginPanel.setLayout(new SpringLayout());
        loginPanel.setName("LoginDialog.loginPanel");
        userName = new JXTextField();
        userName.setToolTipText(USERNAME_TOOLTIP);
        userName.setName("LoginDialog.userName");

        userPassword = new JPasswordField();
        userPassword.setFont(userName.getFont());
        userPassword.setEchoChar('*');
        userPassword.setName("LoginDialog.userPassword");
        userPassword.setToolTipText("Введите пароль");

        loginPanel.add(new JLabel("Логин"));
        loginPanel.add(userName);
        loginPanel.add(new JLabel("Пароль"));
        loginPanel.add(userPassword);

        SpringUtilities.makeCompactGrid(loginPanel, 2, 2, // rows, cols
                0, 10, // initX, initY
                16, 12); // xPad, yPad
    }

    private void createButtons() {
        JPanel p = new JPanel();
        p.setLayout(new BorderLayout());

        loginBtn = new JButton("Войти");
        loginBtn.setName("LoginDialog.loginBtn");
        loginBtn.setDefaultCapable(true);
        getRootPane().setDefaultButton(loginBtn);

        cancelBtn = new JButton("Отмена");

        this.setFocusableWindowState(true);
        Box vb = Box.createVerticalBox();
        vb.add(Box.createVerticalStrut(5));

        Box hb = Box.createHorizontalBox();
        hb.add(Box.createHorizontalStrut(15));
        hb.add(loginBtn);
        hb.add(Box.createHorizontalStrut(10));
        hb.add(cancelBtn);
        hb.add(Box.createHorizontalStrut(20));

        vb.add(hb);
        vb.add(Box.createVerticalStrut(5));

        p.add(vb);

        Box vvb = Box.createVerticalBox();
        hb = Box.createHorizontalBox();
        hb.add(Box.createHorizontalGlue());
        hb.add(Box.createHorizontalStrut(16));
        vvb.add(hb);
        vvb.add(p);
        add(vvb, BorderLayout.SOUTH);
    }

    private void createParamsPanel() {
        paramsPanel = new JPanel();
        paramsPanel.setLayout(new SpringLayout());
        paramsPanel.setName("LoginDialog.paramsPanel");

        ConnectionParams[] props = {ConnectionParams.SERVER_ADDRESS, ConnectionParams.SERVER_PORT};

        for (ConnectionParams param : props) {
            JLabel label = new JLabel(param.toString());
            JTextField textField = new JXTextField();
            map.put(param, textField);
            paramsPanel.add(label);
            paramsPanel.add(textField);
        }

        restoreValues();
        SpringUtilities.makeCompactGrid(paramsPanel, props.length, 2, // rows, cols
                0, 10, // initX, initY
                16, 12); // xPad, yPad

    }

    private void createButtonPanel() {
        buttonPanel = new JPanel(new BorderLayout());
        paramsBtn = new JXHyperlink(new AbstractAction(SHOW_PARAMS) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                toggleParamsPanel();
                loginBtn.requestFocus();
            }
        });

        paramsBtn.setName("LoginDialog.paramsLink");

        Box hb = Box.createHorizontalBox();
        hb.add(paramsBtn);
        hb.add(Box.createHorizontalStrut(16));
        buttonPanel.add(hb, BorderLayout.EAST);
    }

    private void initActions() {
        Action exit = new AbstractAction() {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);
                doCancel();
            }
        };
        cancelBtn.addActionListener(exit);

        loginBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                loginBtn.setEnabled(false);
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        if (checkParams()) {
                            if (login()) {
                                setVisible(false);
                                doLogin();
                            } else {
                                loginBtn.setEnabled(true);
                                showMsg("Введены неправильные входные данные", userPassword);
                            }
                        } else {
                            loginBtn.setEnabled(true);
                        }
                    }
                });
            }
        });

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                doCancel();
            }
        });
    }

    public String getUsername() {
        return userName.getText();
    }

    public void setUsername(String login) {
        userName.setText(login);
    }

    public String getPassword() {
        return new String(userPassword.getPassword());
    }

    public void setPassword(String password) {
        userPassword.setText(password);
    }

    /**
     * Validation input values before login
     *
     * @return true if all valid
     */
    private boolean checkParams() {
        if (userName.getText().trim().equals("")) {
            showMsg("Логин не введен", userName);
            return false;
        }

        if (userPassword.getPassword().length == 0) {
            showMsg("Пароль не введен", userPassword);
            return false;
        }

        for (ConnectionParams p : map.keySet()) {
            String fieldValue = map.get(p).getText();

            // check for empty vals
            if (fieldValue.trim().equals("")) {
                showMsg("Поле " + p.toString() + " не заполнено", map.get(p));
                return false;
            }

            // check ip
            if (p == ConnectionParams.SERVER_ADDRESS) {
                if (!(Validation.checkFQDN(fieldValue) || Validation.checkIpAddress(fieldValue))) {
                    showMsg("Адрес сервера некорректен", map.get(p));
                    return false;
                }
            }

            // check port
            if (p == ConnectionParams.SERVER_PORT) {
                if (!Validation.checkPort(fieldValue)) {
                    showMsg("Порт сервера некорректен", map.get(p));
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Show dialog window with error message
     *
     * @param err error message
     * @param tf text field associated with this error
     */
    private void showMsg(String err, JTextField tf) {
        if (!isShownParams) {
            toggleParamsPanel();
        }
        JOptionPane.showMessageDialog(this, err, "Внимание", JOptionPane.ERROR_MESSAGE);
        tf.requestFocusInWindow();
    }

    /**
     * Show/ hide additional params panel
     */
    private void toggleParamsPanel() {
        if (isShownParams) {
            mainPanel.remove(paramsPanel);
            SpringUtilities.makeCompactGrid(mainPanel, 2, 1, 20, 0, 5, 0);
        } else {
            mainPanel.add(paramsPanel);
            SpringUtilities.makeCompactGrid(mainPanel, 3, 1, 20, 0, 5, 0);
        }

        isShownParams = !isShownParams;
        repaint();
        pack();
    }

    public String getPropertyValue(ConnectionParams prop) {
        return map.get(prop).getText().trim();
    }
}
